{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "check 19199191\n"
     ]
    }
   ],
   "source": [
    "import tiled_fuzzer\n",
    "import codegen\n",
    "import pindef\n",
    "import bslib\n",
    "import chipdb\n",
    "import fuse_h4x\n",
    "import gowin_unpack\n",
    "from wirenames import wirenames\n",
    "from PIL import Image\n",
    "import numpy as np\n",
    "import pickle\n",
    "\n",
    "def dff(mod, cst, row, col, clk=None):\n",
    "    \"make a dff with optional clock\"\n",
    "    name = tiled_fuzzer.make_name(\"DFF\", \"DFF\")\n",
    "    dff = codegen.Primitive(\"DFF\", name)\n",
    "    dff.portmap['CLK'] = clk if clk else name+\"_CLK\"\n",
    "    dff.portmap['D'] = name+\"_D\"\n",
    "    dff.portmap['Q'] = name+\"_Q\"\n",
    "    mod.wires.update(dff.portmap.values())\n",
    "    mod.primitives[name] = dff\n",
    "    cst.cells[name] = f\"R{row}C{col}\"\n",
    "    return dff.portmap['CLK']\n",
    "\n",
    "def ibuf(mod, cst, loc, clk=None):\n",
    "    \"make an ibuf with optional clock\"\n",
    "    name = tiled_fuzzer.make_name(\"IOB\", \"IBUF\")\n",
    "    iob = codegen.Primitive(\"IBUF\", name)\n",
    "    iob.portmap[\"I\"] = name+\"_I\"\n",
    "    iob.portmap[\"O\"] = clk if clk else name+\"_O\"\n",
    "\n",
    "    mod.wires.update([iob.portmap[\"O\"]])\n",
    "    mod.inputs.update([iob.portmap[\"I\"]])\n",
    "    mod.primitives[name] = iob\n",
    "    cst.ports[name] = loc\n",
    "    return iob.portmap[\"O\"]\n",
    "\n",
    "with open(f\"{tiled_fuzzer.gowinhome}/IDE/share/device/{tiled_fuzzer.device}/{tiled_fuzzer.device}.fse\", 'rb') as f:\n",
    "    fse = fuse_h4x.readFse(f)\n",
    "    \n",
    "with open(f\"{tiled_fuzzer.device}.pickle\", 'rb') as f:\n",
    "    db = pickle.load(f)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['IOB10A', 'IOR5A', 'IOR7A', 'IOL7A', 'IOB11A']"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dff_locs = [(2, 18), (2, 5), (10, 5), (10, 18), (10, 18)]\n",
    "clock_pins = pindef.get_clock_locs(\"GW1N-1\", \"QN48\", header=1)\n",
    "# pins appear to be differential with T/C denoting true/complementary\n",
    "true_pins = [p[0] for p in clock_pins if \"GCLKT\" in p[1]]\n",
    "true_pins"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAABMAAAAESAQAAAAAz/5S4AAABX0lEQVR4nO3dTU6DUBQG0NfWaEfK0JkMXIYDl+ISXAJbcEcdupw6c1YH2BgSkgZyKVc4Z/IaAq9feZe/NpRSWIqP57vPuTP8J1VkZ5vIzraRnbEUq6ixOjLYKbCvtGssNFhojUVKG4yhDOVQaff8aYOllXaN5dsq922TL1h2aTckQwkAAISpQr/x7bF5OI5argpN0WPspdU+NEWfZvJ3AAAAAAAAAACYQDV3ABbvZu4ALF49dwAAAAAAYCVe2mb3fp7wdzfu/aEzy5U9ts3u+zzh9u386unQmQUYYvo71Eep5g5AnHruAP38wrsgSQeznjsAAAAAAAAAAAAAAABrcIUnfp6OTc/07cUF47N0fW2qMYtd44mfk392hgodkovFzxqpMaamxpha7OlL1hs/I4s/7VYZenmwDTx3TrvrSTuU+S4qfsshX7Ds1lH8MV7bJuFRpGlKKaXsk/5Dyyr8ANqSEvvmsawWAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=1216x274 at 0x7FADE9F0D070>"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "mod = codegen.Module()\n",
    "cst = codegen.Constraints()\n",
    "\n",
    "ibufs = [ibuf(mod, cst, p) for p in true_pins]\n",
    "dffs = [dff(mod, cst, row, col) for row, col in dff_locs]\n",
    "    \n",
    "\n",
    "bs, hdr, ftr, posp, config = tiled_fuzzer.run_pnr(mod, cst, {})\n",
    "bslib.display(None, bs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAABMAAAAESAQAAAAAz/5S4AAAAtUlEQVR4nO3dwQ2CMBSA4Qfh4LExDqAbOAJuxgpuwKiePGEElZZKvu9c8r80JOVA0ggAAAAA4K1D7kA7fPdcWm8EAIC69bkDXe4AAAAAAAAAAAAAAAAAAAD8m1uM1zjGZei3nmTiHt0p2qZM7JlJuwl9arLR7bL1af1RXisWWqrQqwkAAAAAAAAAAAAAFch+42cz5C4UNvND9u/qvZHrvPUAALA3xY/9vnRwTr0fPgAAAACs4AHt6QTN9d4X+AAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=1216x274 at 0x7FADE9F0D2E0>"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "mod.assigns = zip(dffs, ibufs)\n",
    "bs_clk, hdr, ftr, posp, config = tiled_fuzzer.run_pnr(mod, cst, {})\n",
    "diff = bs^bs_clk\n",
    "tiles = fuse_h4x.tile_bitmap(fse, diff)\n",
    "bslib.display(None, diff)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dict_keys([(1, 4, 12), (1, 17, 12), (5, 9, 82), (5, 10, 83), (6, 3, 14), (6, 4, 14), (6, 17, 13), (6, 18, 13), (9, 3, 12), (9, 4, 12), (9, 17, 12), (9, 18, 12)])"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "tiles.keys()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "dff\n",
      "(1, 4, 12)\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAABgAQAAAAALSMJ8AAAAMElEQVR4nGNgGAWjgOEDlGbCr4wyaeyAnzqGk2X3KBgFo2AUjIJRMAwA/2gdOtwAAChhASG93J4RAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=240x96 at 0x7FAE209557C0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "dff\n",
      "(1, 17, 12)\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAABgAQAAAAALSMJ8AAAAKklEQVR4nGNgGAWj4MMHKIMJvzrKpEfBKBgFo2AUjIJRQAPAP1p/DzcAAGqzAfz09PlkAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=240x96 at 0x7FAE20955880>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(5, 9, 82)\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAABoAQAAAADnG0ARAAAANklEQVR4nO3OsQkAIBBD0Y8L3P5bZgMtrM9GBIv/2gQSkCRJkvSpggJIgAnjXL+LG6n94en2AhgUBBjZTBm8AAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=240x104 at 0x7FAE300A2070>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(5, 10, 83)\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAABoAQAAAADnG0ARAAAAOUlEQVR4nO3NsQkAIBAEwcUGrv8uvwQD4cHERASDnfQ4FiRJkiT9JRQARQpID+P8u5tXKV3b02/bE1DMBDZ9wsHMAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=240x104 at 0x7FAE20955A30>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(6, 3, 14)\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAABgAQAAAAALSMJ8AAAAKUlEQVR4nGNgwAf4mfBKM1AmPXjBSPX3KBgFo2AUjIJRMApGwSgYaAAA2SsAK+z+qbkAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=240x96 at 0x7FAE20955B20>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(6, 4, 14)\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAABgAQAAAAALSMJ8AAAAKUlEQVR4nGNgwAf4mfBKM1AmPXjBSPX3KBgFo2AUjIJRMApGwSgYaAAA2SsAK+z+qbkAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=240x96 at 0x7FAE20955AF0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(6, 17, 13)\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAABgAQAAAAALSMJ8AAAAJElEQVR4nO3JMQ0AAAgDsAUD+HeLA25I2rfJpmvt6G8NAMBtA/f9AC6D172zAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=240x96 at 0x7FAE300A2070>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(6, 18, 13)\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAABgAQAAAAALSMJ8AAAAKUlEQVR4nGNgwAf4mfBKM1AmPXjBSPX3KBgFo2AUjIJRMApGwSgYaAAA2SsAK+z+qbkAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=240x96 at 0x7FAE20955AC0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(9, 3, 12)\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAABgAQAAAAALSMJ8AAAAJklEQVR4nGNgGAWjYBSMglEwCkbBKBgFQxbwMzDhV0CZ9CigPwAAMSkAFgJADGgAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=240x96 at 0x7FAE20955820>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "dff\n",
      "(9, 4, 12)\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAABgAQAAAAALSMJ8AAAALElEQVR4nGNgGAWjgOEDlGbCr4wy6cEIPkCoEefvUTAKRsEoGAWjYBQMXgAAjZcB7Qz3epAAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=240x96 at 0x7FAE209557C0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "dff\n",
      "(9, 17, 12)\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAABgAQAAAAALSMJ8AAAANElEQVR4nGNgGAWjgP8DlMGEXx1l0oMO8MNZA+LvDwNo9ygYBaNgFIyCUUAVwD9aj9ECAAB2SgImuz/CBAAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=240x96 at 0x7FAE20955A00>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(9, 18, 12)\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAABgAQAAAAALSMJ8AAAAJklEQVR4nGNgGAWjYBSMglEwCkbBKBgFQxbwMzDhV0CZ9CigPwAAMSkAFgJADGgAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=240x96 at 0x7FAE20955940>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "for loc, tile in tiles.items():\n",
    "    im = bslib.display(None, tile)\n",
    "    im_scaled = im.resize((im.width*4, im.height*4), Image.NEAREST)\n",
    "    if (loc[0]+1, loc[1]+1) in dff_locs:\n",
    "        print('dff')\n",
    "    print(loc)\n",
    "    display(im_scaled)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "At this point several things are notable.\n",
    "\n",
    " * In the DFF that were placed, a few bits in the routing MUXes changed, 99% sure that's just the connection to `GBxx`, which warants confirmation.\n",
    " * **No bits in the IOB changed**. This suggests they are by default connected to the clock tree? What if you use the complementary pin?\n",
    " * A bunch of bits in the center tile changed, this is probably where the meat of the clock routing happens.\n",
    " * In the center row there are a few tiles with 2 bits changed, most likely connecting a horizontal \"spine\" from the center tile to a vertical \"tap\".\n",
    " * In several tiles there is one bit, most likely connecting the \"tap\" to a horizontal \"branch\".\n",
    " \n",
    "This all has to be further explored.\n",
    "\n",
    "It can be seen that the tiles in which the \"spine\" is connected to the \"taps\" are of a distinct type, namely type 13 and 14.\n",
    "\n",
    "![tile grid](doc/fig/fuse.png)\n",
    "\n",
    "Meanwhile, looking at the bits within a tile of type 12, it can be seen that the single dots correspond with the bottom right cluster, while the bits of type 13 and 14 are not present. There is an empty column there.\n",
    "\n",
    "![tile grid](doc/fig/tile.png)\n",
    "\n",
    "Let's see if tile 13 and 14 indeed have something interesting at these bits."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<PIL.Image.Image image mode=P size=480x192 at 0x7FADEE2DDBE0>"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "tile = fuse_h4x.render_tile(fse, 14)\n",
    "im = fuse_h4x.display(None, tile)\n",
    "im.resize((im.width*8, im.height*8), Image.NEAREST) # THEY DO!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(1, 4), DFF0, DFF1, CLK0: GB10, GBO0: GT00\n",
      "(1, 17), DFF0, DFF1, CLK0: GB00, GBO0: GT00\n",
      "(5, 9)\n",
      "(5, 10)\n",
      "(6, 3)\n",
      "(6, 4)\n",
      "(6, 17)\n",
      "(6, 18)\n",
      "(9, 3), GBO0: GT00\n",
      "(9, 4), DFF0, DFF1, CLK0: GB20\n",
      "(9, 17), DFF0, DFF1, CLK0: GB30, CLK1: GB40, GBO1: GT10\n",
      "(9, 18), GBO0: GT00\n"
     ]
    }
   ],
   "source": [
    "# Lets parse the tile to confirm the MUX\n",
    "bm = chipdb.tile_bitmap(db, bs_clk)\n",
    "interesting = {loc[0:2] for loc in tiles.keys()}\n",
    "for idx, t in bm.items():\n",
    "    if idx in interesting:\n",
    "        row, col = idx\n",
    "        dbtile = db.grid[row][col]\n",
    "        bels, pips = gowin_unpack.parse_tile_(dbtile, t)\n",
    "        print(repr(idx) +\n",
    "              (\", DFF0\" if 'DFF0' in bels else \"\") +\n",
    "              (\", DFF1\" if 'DFF1' in bels else \"\") +\n",
    "              (\", CLK0: \" + pips['CLK0'] if pips['CLK0'] != \"VCC\" else \"\") +\n",
    "              (\", CLK1: \" + pips['CLK1'] if pips['CLK1'] != \"VCC\" else \"\") +\n",
    "              (\", GBO0: \" + pips.get('GBO0') if pips.get('GBO0') else \"\") +\n",
    "              (\", GBO1: \" + pips.get('GBO1') if pips.get('GBO1') else \"\")\n",
    "        )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So this confirms that `CLK0` is indeed connected to `GBxx`, and in fact 4 diffrent `GBxx`.\n",
    "It also shows that we're already decoding the `GBO0` to `GT00` connection, which I'm 99% sure corresponds to the bottom right bit.\n",
    "What it all means? Nobody knows. Okay, maybe some god and a few Gowin engineers...\n",
    "\n",
    "The fact that there is only one `GBO0` to `GT00` connection would suggest it routes all `GBxx` and then selects one locally.\n",
    "But what's weird is that it seems to route each via a seperate tap, while the DFF are in the same column. Would it then not be more sense to enable one tap for both?\n",
    "Maybe this is to limit clock skew?\n",
    "\n",
    "Next step is to dig in the fuse file and confirm the `GBO0` to `GT00` connection and inspect the top right bits in tiles 13 and 14."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[(0, 59), (2, 59)]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'GT00': {'A6': {(0, 59), (2, 59)},\n",
       "  'B6': {(0, 59), (2, 59)},\n",
       "  'C6': {(0, 59), (2, 59)},\n",
       "  'D6': {(0, 59), (2, 59)}},\n",
       " 'GT10': {'A7': {(1, 59), (3, 59)},\n",
       "  'B7': {(1, 59), (3, 59)},\n",
       "  'C7': {(1, 59), (3, 59)},\n",
       "  'D7': {(1, 59), (3, 59)},\n",
       "  'N130': {(3, 59)},\n",
       "  'EW20': {(1, 59), (3, 59)},\n",
       "  'E130': {(1, 59), (3, 59)},\n",
       "  'W100': {(1, 59), (3, 59)},\n",
       "  'W130': {(1, 59)},\n",
       "  'N200': {(1, 59), (3, 59)},\n",
       "  'N210': {(1, 59), (3, 59)},\n",
       "  'N220': {(1, 59), (3, 59)},\n",
       "  'N230': {(1, 59), (3, 59)}}}"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "spine_tile = tiles[(6, 4, 14)]\n",
    "#fs = fuse_h4x.scan_fuses(fse, 14, spine_tile)\n",
    "loc = list(zip(*np.where(spine_tile==1)))\n",
    "print(loc)\n",
    "#fuses = fuse_h4x.scan_tables(fse, 14, fs)\n",
    "#parsed = fuse_h4x.parse_tile(fse, 14, spine_tile)\n",
    "# stolen from chipdb.py\n",
    "def fse_pips(fse, ttyp, table):\n",
    "    pips = {}\n",
    "    for srcid, destid, *fuses in fse[ttyp]['wire'][table]:\n",
    "        fuses = {fuse_h4x.fuse_lookup(fse, ttyp, f) for f in chipdb.unpad(fuses)}\n",
    "        if srcid < 0:\n",
    "            fuses = set()\n",
    "            srcid = -srcid\n",
    "        #if srcid > 1000:\n",
    "        #    srcid -= 1000 # what does it mean?\n",
    "        #if destid > 1000:\n",
    "        #    destid -= 1000 # what does it mean?\n",
    "        src = wirenames[srcid]\n",
    "        dest = wirenames[destid]\n",
    "        pips.setdefault(dest, {})[src] = fuses\n",
    "    return pips\n",
    "    \n",
    "fse_pips(fse, 13, 38)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That is... somewhat confusing. But hey, it seems that wire table 38 contains the fuses for the top right bits, which drive `GT00`.\n",
    "It's worth noting the source wire names might not be accurate, this is after all a completely different wire table.\n",
    "Normal routing is in table 2.\n",
    "\n",
    "Note also from `wirenames` that there are just two `GBO0`/`GBO1` and two `GT00`/`GT10`, while there are 8 `GBxx`. (note `O`/`0`).\n",
    "Does this match with primary/secondary naming in the timing info? Seems plausible.\n",
    "\n",
    "So okay, I can get into this. *something* drives the vertical `GT00` wires, which drives the horizontal `GBO0` wires, which drives the `GBxx` inputs at th DFF. I think it's time to look at this big and scary center tiles."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Wire tables: [2, 38] [2, 38]\n",
      "Tile 82 destinations: ['A4', 'B4', 'C4', 'D4', 'A5', 'B5', 'N100', 'SN10', 'SN20', 'N130', 'F0', 'F1', 'F6', 'F2', 'F3', 'F4', 'F5']\n",
      "Tile 14 source:       [['A4', 'B4', 'C4', 'D4'], ['A5', 'B5', 'C5', 'D5', 'N100', 'SN10', 'SN20', 'N130', 'S100', 'S130', 'E100', 'EW10']]\n",
      "Tile 83 destinations: ['A6', 'B6', 'C6', 'D6', 'A7', 'B7', 'EW20', 'E130', 'W100', 'W130', 'Q0', 'Q1', 'Q6', 'Q2', 'Q3', 'Q4', 'Q5']\n",
      "Tile 13 source:       [['A6', 'B6', 'C6', 'D6'], ['A7', 'B7', 'C7', 'D7', 'N130', 'EW20', 'E130', 'W100', 'W130', 'N200', 'N210', 'N220', 'N230']]\n"
     ]
    }
   ],
   "source": [
    "print(\"Wire tables:\", list(fse[82]['wire'].keys()), list(fse[83]['wire'].keys())) # table 38 again!\n",
    "print(\"Tile 82 destinations:\", list(fse_pips(fse, 82, 38).keys()))\n",
    "print(\"Tile 14 source:      \", [list(k.keys()) for k in fse_pips(fse, 14, 38).values()])\n",
    "print(\"Tile 83 destinations:\", list(fse_pips(fse, 83, 38).keys()))\n",
    "print(\"Tile 13 source:      \", [list(k.keys()) for k in fse_pips(fse, 13, 38).values()])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is interesting, it appears that despite the awkward names, most of the routes actually match. The puzzling part is the sources of the center tiles.\n",
    "Somehow these have to match up with dedicated clock pins and other clock sources.\n",
    "If the taps and branches indeed switch all 8 global clocks at once, there should be a bunch of different muxes in there.\n",
    "How is this encoded in the fuse file? No idea. More poking around needed..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<PIL.Image.Image image mode=P size=480x208 at 0x7FADE9F0D130>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAeAAAAAQAQAAAADmSpGVAAAAMklEQVR4nGNgQID/DAz/UbkMDP//o3BRAROmEPFgVDM9NUPAfyiJEZf4YnnI+nkkagYAe6ELEi95Wt8AAAAASUVORK5CYII=\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=480x16 at 0x7FADE9F0D130>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<PIL.Image.Image image mode=P size=480x208 at 0x7FADE9F0D2B0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAeAAAAAQAQAAAADmSpGVAAAAMklEQVR4nGNgwAf+I5EMDAz/4cz//xkYGBiY8GomAEY101MzSeA/JM7h8T5E/TwSNQMAGcsJFAsyyAsAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=480x16 at 0x7FADE9F0D2B0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "tile = fuse_h4x.render_tile(fse, 83)\n",
    "im = fuse_h4x.display(None, tile)\n",
    "display(im.resize((im.width*8, im.height*8), Image.NEAREST))\n",
    "\n",
    "im = bslib.display(None, tiles[(5, 10, 83)][-2:,:])\n",
    "display(im.resize((im.width*8, im.height*8), Image.NEAREST))\n",
    "\n",
    "tile = fuse_h4x.render_tile(fse, 82)\n",
    "im = fuse_h4x.display(None, tile)\n",
    "display(im.resize((im.width*8, im.height*8), Image.NEAREST))\n",
    "\n",
    "im = bslib.display(None, tiles[(5, 9, 82)][-2:,:])\n",
    "display(im.resize((im.width*8, im.height*8), Image.NEAREST))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAeAAAADQAQAAAAD3oo8hAAAAS0lEQVR4nO3RwQbAQAwE0Nn+/z9PD6v2Vqos1fcOEWJEJAEAAAAAAAAAAAAAYIdxO+1YNUnH1TZJcrzZLLwz/Ejng+eT26/e/MfwCR02ChjdYUvkAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=480x208 at 0x7FAE209679A0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAeAAAADQAQAAAAD3oo8hAAAATElEQVR4nO3RwQbAMBAE0JX//+fpYZVG6CWE6nunjTVWTBUAAAAAAAAAAAAAAEckyT1W5bnp9fScjZ3DwifD3WR3mLXLt5Y/++c/hi+YfREMmdxY0gAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=480x208 at 0x7FAE209677C0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# just to verify that the tile itself rather than the diff doesn't have more interesting stuff.\n",
    "# it does not. A few const bits that are always there.\n",
    "tiles_clk = fuse_h4x.tile_bitmap(fse, bs_clk)\n",
    "im = bslib.display(None, tiles_clk[(5, 9, 82)])\n",
    "display(im.resize((im.width*8, im.height*8), Image.NEAREST))\n",
    "im = bslib.display(None, tiles_clk[(5, 10, 83)])\n",
    "display(im.resize((im.width*8, im.height*8), Image.NEAREST))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "({'GT00': {(20, 57)}}, {'GT10': {(21, 57)}})"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p = fse_pips(fse, 14, 2)\n",
    "p['GBO0'], p['GBO1'] # there is only one option here"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "# lets do some dumb fuzzing!\n",
    "from multiprocessing.dummy import Pool\n",
    "\n",
    "mod = codegen.Module()\n",
    "cst = codegen.Constraints()\n",
    "ibuf(mod, cst, true_pins[2], clk=\"myclk\")\n",
    "base_bs, hdr, ftr, posp, config = tiled_fuzzer.run_pnr(mod, cst, {})\n",
    "\n",
    "modules = []\n",
    "constrs = []\n",
    "for i in range(2, 20):\n",
    "    mod = codegen.Module()\n",
    "    cst = codegen.Constraints()\n",
    "\n",
    "    ibuf(mod, cst, true_pins[0], clk=\"myclk\")\n",
    "    dff(mod, cst, 8, i, clk=\"myclk\")\n",
    "    \n",
    "    modules.append(mod)\n",
    "    constrs.append(cst)\n",
    "\n",
    "\n",
    "p = Pool()\n",
    "pnr_res = p.map(lambda param: tiled_fuzzer.run_pnr(*param, {}), zip(modules, constrs))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "################################################################################\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAAAIAQAAAADv709LAAAAHElEQVR4nGNgwAE+MDAwMDAw4ZJmoLH0BxrbDQAy3wHvbt/zeAAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=240x8 at 0x7FAE209679D0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tap 1\n",
      "branch 1\n",
      "################################################################################\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAAAIAQAAAADv709LAAAAHElEQVR4nGNgwAE+MDAwMDAw4ZJmoLH0BxrbDQAy3wHvbt/zeAAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=240x8 at 0x7FAE209553A0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tap 1\n",
      "branch 1\n",
      "branch 2\n",
      "################################################################################\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAAAIAQAAAADv709LAAAAHElEQVR4nGNgwAE+MDAwMDAw4ZJmoLH0BxrbDQAy3wHvbt/zeAAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=240x8 at 0x7FAE20955CD0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tap 1\n",
      "branch 1\n",
      "branch 3\n",
      "################################################################################\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAAAIAQAAAADv709LAAAAHElEQVR4nGNgwAE+MDAwMDAw4ZJmoLH0BxrbDQAy3wHvbt/zeAAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=240x8 at 0x7FAE20955460>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tap 5\n",
      "branch 4\n",
      "branch 5\n",
      "################################################################################\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAAAIAQAAAADv709LAAAAHElEQVR4nGNgwAE+MDAwMDAw4ZJmoLH0BxrbDQAy3wHvbt/zeAAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=240x8 at 0x7FAE209557C0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tap 5\n",
      "branch 5\n",
      "################################################################################\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAAAIAQAAAADv709LAAAAHElEQVR4nGNgwAE+MDAwMDAw4ZJmoLH0BxrbDQAy3wHvbt/zeAAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=240x8 at 0x7FAE20955940>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tap 5\n",
      "branch 5\n",
      "branch 6\n",
      "################################################################################\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAAAIAQAAAADv709LAAAAHElEQVR4nGNgwAE+MDAwMDAw4ZJmoLH0BxrbDQAy3wHvbt/zeAAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=240x8 at 0x7FAE20955B20>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tap 5\n",
      "branch 5\n",
      "branch 7\n",
      "################################################################################\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAAAIAQAAAADv709LAAAAHElEQVR4nGNgwAE+MDAwMDAw4ZJmoLH0BxrbDQAy3wHvbt/zeAAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=240x8 at 0x7FAE209553A0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tap 5\n",
      "branch 5\n",
      "branch 8\n",
      "################################################################################\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAAAIAQAAAADv709LAAAAHElEQVR4nGNgwAE+MDAwMDAw4ZJmoLH0BxrbDQAy3wHvbt/zeAAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=240x8 at 0x7FAE20955CD0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tap 5\n",
      "branch 5\n",
      "branch 9\n",
      "################################################################################\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAAAIAQAAAADv709LAAAAHElEQVR4nGNgYGBgYOBnwAGYcElQSRqnxbS2GwAdBQAttXhucAAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=240x8 at 0x7FAE20955AF0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tap 13\n",
      "branch 10\n",
      "branch 13\n",
      "################################################################################\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAAAIAQAAAADv709LAAAAHElEQVR4nGNgYGBgYOBnwAGYcElQSRqnxbS2GwAdBQAttXhucAAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=240x8 at 0x7FAE20955460>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tap 13\n",
      "branch 11\n",
      "branch 13\n",
      "################################################################################\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAAAIAQAAAADv709LAAAAHElEQVR4nGNgYGBgYOBnwAGYcElQSRqnxbS2GwAdBQAttXhucAAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=240x8 at 0x7FAE20955B20>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tap 13\n",
      "branch 12\n",
      "branch 13\n",
      "################################################################################\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAAAIAQAAAADv709LAAAAHElEQVR4nGNgYGBgYOBnwAGYcElQSRqnxbS2GwAdBQAttXhucAAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=240x8 at 0x7FAE20955AC0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tap 13\n",
      "branch 13\n",
      "################################################################################\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAAAIAQAAAADv709LAAAAHElEQVR4nGNgYGBgYOBnwAGYcElQSRqnxbS2GwAdBQAttXhucAAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=240x8 at 0x7FAE209554F0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tap 13\n",
      "branch 13\n",
      "branch 14\n",
      "################################################################################\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAAAIAQAAAADv709LAAAAHElEQVR4nGNgYGBgYOBnwAGYcElQSRqnxbS2GwAdBQAttXhucAAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=240x8 at 0x7FAE20955CD0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tap 13\n",
      "branch 13\n",
      "branch 15\n",
      "################################################################################\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAAAIAQAAAADv709LAAAAHElEQVR4nGNgYGBgYOBnwAGYcElQSRqnxbS2GwAdBQAttXhucAAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=240x8 at 0x7FAE209557C0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tap 17\n",
      "branch 16\n",
      "branch 17\n",
      "################################################################################\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAAAIAQAAAADv709LAAAAHElEQVR4nGNgYGBgYOBnwAGYcElQSRqnxbS2GwAdBQAttXhucAAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=240x8 at 0x7FAE20955940>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tap 17\n",
      "branch 17\n",
      "################################################################################\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPAAAAAIAQAAAADv709LAAAAHElEQVR4nGNgYGBgYOBnwAGYcElQSRqnxbS2GwAdBQAttXhucAAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=240x8 at 0x7FAE209553A0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tap 17\n",
      "branch 17\n",
      "branch 18\n"
     ]
    }
   ],
   "source": [
    "for sweep_bs, *rest in pnr_res:\n",
    "    sweep_tiles = fuse_h4x.tile_bitmap(fse, sweep_bs^base_bs)\n",
    "    \n",
    "    print(\"#\"*80)\n",
    "    for loc, tile in sweep_tiles.items():\n",
    "        row, col, ttyp = loc\n",
    "        dbtile = db.grid[row][col]\n",
    "        bels, pips = gowin_unpack.parse_tile_(dbtile, tile)\n",
    "        if ttyp == 12:\n",
    "            print(\"branch\", col)\n",
    "            pass\n",
    "        elif ttyp in {13, 14}:\n",
    "            print(\"tap\", col)\n",
    "        elif ttyp in {82, 83}:\n",
    "            im = bslib.display(None, tile[-2:,:])\n",
    "            im_scaled = im.resize((im.width*4, im.height*4), Image.NEAREST)\n",
    "            display(im_scaled)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "# that's... not what I expected. I changed a few things but it seems pretty consistent.\n",
    "# lemme try changing between clock inputs to see if ANYTHING changes...\n",
    "\n",
    "mod = codegen.Module()\n",
    "cst = codegen.Constraints()\n",
    "\n",
    "clks = [ibuf(mod, cst, p) for p in true_pins]\n",
    "flop = dff(mod, cst, 2, 2)\n",
    "\n",
    "bs_base, hdr, ftr, posp, config = tiled_fuzzer.run_pnr(mod, cst, {})\n",
    "\n",
    "clk_bss = []\n",
    "for clk in clks:\n",
    "    mod.assigns = [(flop, clk)]\n",
    "    bs_clkn, hdr, ftr, posp, config = tiled_fuzzer.run_pnr(mod, cst, {})\n",
    "    clk_bss.append(bs_clkn)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAeAAAAAQAQAAAADmSpGVAAAAIElEQVR4nGNgIBH8R2IzkaoZGYxqHgKa/w+YzaOayQIAh/sCHc65kO0AAAAASUVORK5CYII=\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=480x16 at 0x7FAE20955AC0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAeAAAAAQAQAAAADmSpGVAAAAIUlEQVR4nGNgIBH8R2IzkaoZGYxqHvya//8fKJtHNZMHADhnAxx8vd8hAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=480x16 at 0x7FAE20835EB0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAeAAAAAQAQAAAADmSpGVAAAAJ0lEQVR4nGNgIBH8R2IzkaoZGYxqpqdmFrJ0/WdgYKTU5lHN9NQMAI55AiCuD616AAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=480x16 at 0x7FAE20857A60>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAeAAAAAQAQAAAADmSpGVAAAAIklEQVR4nGNgIBH8R2IzkaoZGYxqHvya/0Oie6g5ewRrBgA3aAMcAErq/wAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=480x16 at 0x7FAE20955AC0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAeAAAAAQAQAAAADmSpGVAAAAIUlEQVR4nGNgIBH8R2IzkaoZGYxqHgKa//8fKJtHNZMFADZpAxx4LYxVAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=480x16 at 0x7FAE20835EB0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAeAAAAAQCAMAAAD571wKAAADAFBMVEUNFmgb5gmJVdooVlU+AG8WmBFQcucXJMoF6ZJD1sRXzTSbhs9QZcSDr6Oq43yrXwjBLtHpqCtWZBJ0klYwFqADWjqIueij/fahO3st/XHACyIxQVpFb2sbEFoWbgKJErFn/EN4wFVZo/jia7etXzwU4Q6EiSWn6sZjIPmEoc1iDyKrWd69+qtNygeF34MjgIte5FdFgTRSZfAUMjP+5DGQFVFXie3PiMl7u8ZB1ZN8ZTmAIKLlypt+sh8N3Fyra1vFV8DSLvoPaoDULI9e6x9D0VLxDWMbkvZTpURwkj+wgOdG9C4Fqbt6Cv8k3uQ43Xbm48ZJlPtSYrAQQCtqbJt/KguGw+3WTd13SBdxi0cx8BHwaMockcJvW34xNj0J6BLtAqeMCvG74W9KLxyr0jYGXCc5OKlvj2P7fwoju+kgddyNBIVJNu8b1MCXUxWr9S4gedkByRYIJXXvqQ8mdherW/TR0TxuqxYCU2bJ0A4Q7EkgysiaxNzaQ2v8XqDY4JpZsABZwqUUuJxxGpi+MqISNFvHSOP7KQa6w+asx10Z/5pF5BIOQSFpnoSNHoj5VMyjw6hwPBccGcTW0eJbrNEhADXqziHYLEQr4tMQBspkkaOLME+YyFYFJUj2bX9s3cwnqozlEJQwkdFviF3x3BYKiE8HrCekGbbWzOKdVS6o+agaCybNIrSVXWtDaS+HqYz97OOaJMLxmXSnoNN6X853pUiJkJbJVitflX1fay0aqBKS+YG2w3yGO6CGVfChaufD+FZ7PLrXsLYk7iPOKq++LY4/8y77gAEnWa2jOW3ZeFGj3bL6cQMJ+F3H+Iysj7rSsaoIIHLQ1m3WWaYg9FtPEjQMjvoCfGu0EDBZVGp9ZVvwGkGxXjDdbKXr7W9yCg3v8mTVu14Qmx5xgAg0SbMmmlyI3Z3WNT15OE/GMntUoHbob+kUZHcZ39hhkIc7DrLKcZybxiyF6Gq8FWIQd6EiK7Mg9sxqKUQ70UuWDN7txJwGKFWqKQo+WBXpbxiODKgsI81GAAAAvElEQVR4nO3SPQ6CUBREYQrXBAq9IroO1P1vwJjMacbcXHvndLw/5X0MQ9PT8vFX0aPIz7+rXc2K9Twvin03tSnG56Kj4vc4j/0ntSrWXy3GL0Vnxf9hPedPivlR+T7G/b4m69DU+QY4wAEOcIADHOAABzjAAQ7wvwNzAR1g9QHsTexzeMBYB0AHTP4ei8UF836bBeBqcZ7Pcx7zvwIT4xWwQ45FFfQXbIADHOAABzjAAQ5wgAMc4AAH+NMbo+Ru29cQQRMAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<PIL.Image.Image image mode=P size=480x16 at 0x7FAE20857610>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "for mybs in clk_bss:\n",
    "    clk_tiles = fuse_h4x.tile_bitmap(fse, mybs^bs_base)\n",
    "    im = bslib.display(None, clk_tiles[(5, 9, 82)][-2:,:])\n",
    "    display(im.resize((im.width*8, im.height*8), Image.NEAREST))\n",
    "    \n",
    "tile = fuse_h4x.render_tile(fse, 82)\n",
    "im = fuse_h4x.display(None, tile[-2:,:])\n",
    "display(im.resize((im.width*8, im.height*8), Image.NEAREST))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Fascinating... so if I sweep a DFF along all the colums, it basically takes the clock from taps 1, 5, 13, 17. The only change in the center mux is between left/right.\n",
    "When instead I sweep the clock pin used, it only changes a few bits in the center mux. So I'm kinda assuming that particular mux is hardwired to these two taps on each side, and other muxes drive other taps along the spine.\n",
    "\n",
    "Since I don't have a trivial way to make it use any other mux voluntarily, fuzzing this will be a big PITA. It probably pays off to look at the vendor data more deeply.\n",
    "\n",
    "I wonder what happens if I fill a whole row with DFF. Many rows? The whole FPGA? What if I fill a whole row with each different clock pin, and then cycle the pins around? Time to find out."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "mod = codegen.Module()\n",
    "cst = codegen.Constraints()\n",
    "\n",
    "clks = [ibuf(mod, cst, p) for p in true_pins]\n",
    "\n",
    "for i in range(2, 7):\n",
    "    for j in range(2, 20):\n",
    "        if i < 6:\n",
    "            flop = dff(mod, cst, i, j)\n",
    "        else:\n",
    "            flop = dff(mod, cst, 2+i, j)\n",
    "\n",
    "bs_base, hdr, ftr, posp, config = tiled_fuzzer.run_pnr(mod, cst, {})\n",
    "\n",
    "mod = codegen.Module()\n",
    "cst = codegen.Constraints()\n",
    "\n",
    "clks = [ibuf(mod, cst, p) for p in true_pins]\n",
    "\n",
    "for i in range(2, 7):\n",
    "    for j in range(2, 20):\n",
    "        if i < 6:\n",
    "            flop = dff(mod, cst, i, j)\n",
    "        else:\n",
    "            flop = dff(mod, cst, 2+i, j)\n",
    "        mod.assigns.append((flop, clks[0]))\n",
    "\n",
    "bs_oneclk, hdr, ftr, posp, config = tiled_fuzzer.run_pnr(mod, cst, {})\n",
    "\n",
    "mod = codegen.Module()\n",
    "cst = codegen.Constraints()\n",
    "\n",
    "clks = [ibuf(mod, cst, p) for p in true_pins]\n",
    "\n",
    "for i, clk in enumerate(clks):\n",
    "    for j in range(2, 20):\n",
    "        if i < 4:\n",
    "            flop = dff(mod, cst, 2+i, j)\n",
    "        else:\n",
    "            flop = dff(mod, cst, 4+i, j)\n",
    "        mod.assigns.append((flop, clk))\n",
    "\n",
    "bs_allclks, hdr, ftr, posp, config = tiled_fuzzer.run_pnr(mod, cst, {})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAABMAAAAESAQAAAAAz/5S4AAAAkklEQVR4nO3auw3DMAwFQIZIkdIjuMyYHD0FCw/gD+H4rnkQCLBQ9wRFAAAAAAAtq/M7ewQAeLD3junUZoBNVqdeCQAwRa8E7i2rU68EAJiiVwL3ltWpVwIATNErAQAAgD/1iViO25bHrYr1zJeR147p1OZZbgwAAACuk9Xp5zAAwBQ/hwEAAAAAAAAAAAAA4Hw/SHYeeeqlhTYAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=1216x274 at 0x7FAE20857AC0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAABMAAAAESAQAAAAAz/5S4AAAA70lEQVR4nO3cu23DMBAAUEpw4SKFOrcqU3oEjsZFUmWKDOBdMkYKxh/BQBJFQo5W3msOpyMIioUAgqRSAgAAAACqvtT4HJsCAPxjuwXVqJ4BbpQa9sHpne4zDrEpAECYHFRtlFUwNKzU0Ny6sl1jDX1wCgBs1pJNRhuUQIRSg3XlT50/xzk2BQA2KDdZBQAAAPiVvjul9+PTIb2WHD2WqZLT27h7SX33fdt5hhrWuEu3YldXX7/wkumY2fPlHOUwfT5M2/919V4zMwYAAAArKjU4Ofzwoq/IjnMGCwA8BL/2BQAAAAAAAAAAAABgQz4Ak5wn6ekjxMgAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=1216x274 at 0x7FAE20857160>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(bslib.display(None, bs_oneclk^bs_base))\n",
    "display(bslib.display(None, bs_allclks^bs_base))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "At this point I kinda understand the single-clock case well enough. The two inner muxes select a clock source, and route it to columns 1, 5, 13, 17. From there two fuses enable the tap, and at each row the single fuse enables the branch for the cells around it. A bit up we saw that columns 1-3 are controlled from tap 1, 4-9 from tap 5, 10-15 from tap 13, and 16-18 from tap 17.\n",
    "\n",
    "For the case of multiple clocks, we can see that all GT00 (primary?) spines are enabled, and the 5th pin is connected to GT10. This lines up with the one-clock case. Would it be the case that GB00-GB30 are GT00 and GB40-GB70 are GT10? It seems like that so far. What if I do 4 clocks, and then change one pin at a time and see what changes?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAABMAAAAESAQAAAAAz/5S4AAAAxElEQVR4nO3bIQ7CQBAF0KFBIBB1WCSSI9RwD45SxUG4EbfgDhjEAiHBELrJbMp75mcyyYgVm0y2jQAAAAAAim4sucstAQD+2HJCN2sywJuxxCq5/LB4ZJ9bAgCkGZK6jbIFQ8PGEs3tle3aluiSSwBgtqY8MnqgBDKMJeyV33pex0NuCQDM0NBkFwAAAOBHh7ju15s415pX7eO7W1wiTnGsNe+lL1HjX7qKo1rWl3BiAAAAAAAAAAAAAAAAAAAAAABAPXeKOyEMn4gWfQAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=1216x274 at 0x7FAE2083E8B0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAABMAAAAESAQAAAAAz/5S4AAAAvUlEQVR4nO3bMQrCMBQG4FgcOmYQXB17jCzeKxdx8RbeyGM4REHoIrbyQv2+5efxIJQOgUeSlAAAAACAZqgtp9gSAOCP7Rd0o1YGeFNbjMHlzO6ZObYEAAhTgrqdMgVDx2qL7ubKfp1aDMElALBZSw4ZHVACEWoLc+WnXttxiS0BgA0qXXYBAAAAvnRO9zwe0zX6O2ZKuk2Hyw8u8+UWa7ylW3GpnuUW/hgAAAAAAAAAAAAAAAAAAAAAALCeB9v4IAjm4PyXAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=1216x274 at 0x7FAE20955790>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAABMAAAAESAQAAAAAz/5S4AAAAvklEQVR4nO3bsQrCMBAG4CgdHBy6uXZ0dHXL4nv1QZx8Oh/D4RSELmIrF+r3LT/HQSgdAkeSUgAAAACAsB0jj7klAMAf62Z0s1YGeDNG7JLLic0z+9wSACBNTeo2yhQMDRsjmpsr2zVEbJNLAGC15hwyOqAEMowR5spPvbbjmlsCACtUm+wCAAAAfOlS7qf9odyyv2OilvPQXX9wma+PWOIt3YJLtayP8McAAAAAAAAAAAAAAAAAAAAAAIDlPADtDx+TJ/tBKAAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=1216x274 at 0x7FAE209553D0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAABMAAAAESAQAAAAAz/5S4AAAAvUlEQVR4nO3bsQrCMBAG4CgdHBy6uHd09BG6+F55E8HJV3RzOAWhi9jKhfp9y89xEEqHwJGkFAAAAAAgbGvkMbcEAPhj3Yxu1soAb2rELrmc2Dyzzy0BANKMSd1GmYKhYTWiubmyXUPENrkEAFZrziGjA0ogQ40wV37qtR2PuSUAsEJjk10AAACAL53L7bQ/lGv2d0yM5T50lx9c5usjlnhLt+BSLesj/DEAAAAAAAAAAAAAAAAAAAAAAGA5D0c+IBT9LqanAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=1216x274 at 0x7FAE209553D0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAABMAAAAESAQAAAAAz/5S4AAAAv0lEQVR4nO3bMQrCQBAF0FVSWFiks01p6RHSeK+9iI238EYew2IUhDRiIrPE95rPMLCEFAvD7pYCAAAAAIRtjTzmlgAAf6yb0c1aGeBNjdgllxObZ/a5JQBAmjGp2yhTMDSsRjQ3V7ZriNgmlwDAas05ZHRACWSoEebKT7224zG3BABWaGyyCwAAAPClc7mf9odyzf6OibHchu7yg8t8fcQSb+kWXKplfYQ/BgAAAAAAAAAAAAAAAAAAAAAALOcBbrAgFEiedRoAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=1216x274 at 0x7FAE20955C70>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "mod = codegen.Module()\n",
    "cst = codegen.Constraints()\n",
    "\n",
    "clks = [ibuf(mod, cst, p) for p in true_pins]\n",
    "\n",
    "for i in range(2, 6):\n",
    "    for j in range(2, 20):\n",
    "        flop = dff(mod, cst, i, j)\n",
    "\n",
    "bs_base, hdr, ftr, posp, config = tiled_fuzzer.run_pnr(mod, cst, {})\n",
    "\n",
    "clk_sweep_bs = []\n",
    "for k in range(5):\n",
    "    mod = codegen.Module()\n",
    "    cst = codegen.Constraints()\n",
    "\n",
    "    clks = [ibuf(mod, cst, p) for p in true_pins]\n",
    "\n",
    "    for i, clk in enumerate(clks[:-1]):\n",
    "        for j in range(2, 20):\n",
    "            flop = dff(mod, cst, 2+i, j)\n",
    "            if i == k:\n",
    "                mod.assigns.append((flop, clks[-1]))\n",
    "            else:\n",
    "                mod.assigns.append((flop, clk))\n",
    "\n",
    "    bs_tmp, hdr, ftr, posp, config = tiled_fuzzer.run_pnr(mod, cst, {})\n",
    "    clk_sweep_bs.append(bs_tmp)\n",
    "\n",
    "for mybs in clk_sweep_bs:\n",
    "    display(bslib.display(None, mybs^bs_base))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAeAAAAAQCAMAAAD571wKAAADAFBMVEUNFmgb5gmJVdooVlU+AG8WmBFQcucXJMoF6ZJD1sRXzTSbhs9QZcSDr6Oq43yrXwjBLtHpqCtWZBJ0klYwFqADWjqIueij/fahO3st/XHACyIxQVpFb2sbEFoWbgKJErFn/EN4wFVZo/jia7etXzwU4Q6EiSWn6sZjIPmEoc1iDyKrWd69+qtNygeF34MjgIte5FdFgTRSZfAUMjP+5DGQFVFXie3PiMl7u8ZB1ZN8ZTmAIKLlypt+sh8N3Fyra1vFV8DSLvoPaoDULI9e6x9D0VLxDWMbkvZTpURwkj+wgOdG9C4Fqbt6Cv8k3uQ43Xbm48ZJlPtSYrAQQCtqbJt/KguGw+3WTd13SBdxi0cx8BHwaMockcJvW34xNj0J6BLtAqeMCvG74W9KLxyr0jYGXCc5OKlvj2P7fwoju+kgddyNBIVJNu8b1MCXUxWr9S4gedkByRYIJXXvqQ8mdherW/TR0TxuqxYCU2bJ0A4Q7EkgysiaxNzaQ2v8XqDY4JpZsABZwqUUuJxxGpi+MqISNFvHSOP7KQa6w+asx10Z/5pF5BIOQSFpnoSNHoj5VMyjw6hwPBccGcTW0eJbrNEhADXqziHYLEQr4tMQBspkkaOLME+YyFYFJUj2bX9s3cwnqozlEJQwkdFviF3x3BYKiE8HrCekGbbWzOKdVS6o+agaCybNIrSVXWtDaS+HqYz97OOaJMLxmXSnoNN6X853pUiJkJbJVitflX1fay0aqBKS+YG2w3yGO6CGVfChaufD+FZ7PLrXsLYk7iPOKq++LY4/8y77gAEnWa2jOW3ZeFGj3bL6cQMJ+F3H+Iysj7rSsaoIIHLQ1m3WWaYg9FtPEjQMjvoCfGu0EDBZVGp9ZVvwGkGxXjDdbKXr7W9yCg3v8mTVu14Qmx5xgAg0SbMmmlyI3Z3WNT15OE/GMntUoHbob+kUZHcZ39hhkIc7DrLKcZybxiyF6Gq8FWIQd6EiK7Mg9sxqKUQ70UuWDN7txJwGKFWqKQo+WBXpbxiODKgsI81GAAAAvElEQVR4nO3SPQ6CUBREYQrXBAq9IroO1P1vwJjMacbcXHvndLw/5X0MQ9PT8vFX0aPIz7+rXc2K9Twvin03tSnG56Kj4vc4j/0ntSrWXy3GL0Vnxf9hPedPivlR+T7G/b4m69DU+QY4wAEOcIADHOAABzjAAQ7wvwNzAR1g9QHsTexzeMBYB0AHTP4ei8UF836bBeBqcZ7Pcx7zvwIT4xWwQ45FFfQXbIADHOAABzjAAQ5wgAMc4AAH+NMbo+Ru29cQQRMAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<PIL.Image.Image image mode=P size=480x16 at 0x7FAE20848970>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAeAAAAAQCAMAAAD571wKAAADAFBMVEUNFmgb5gmJVdooVlU+AG8WmBFQcucXJMoF6ZJD1sRXzTSbhs9QZcSDr6Oq43yrXwjBLtHpqCtWZBJ0klYwFqADWjqIueij/fahO3st/XHACyIxQVpFb2sbEFoWbgKJErFn/EN4wFVZo/jia7etXzwU4Q6EiSWn6sZjIPmEoc1iDyKrWd69+qtNygeF34MjgIte5FdFgTRSZfAUMjP+5DGQFVFXie3PiMl7u8ZB1ZN8ZTmAIKLlypt+sh8N3Fyra1vFV8DSLvoPaoDULI9e6x9D0VLxDWMbkvZTpURwkj+wgOdG9C4Fqbt6Cv8k3uQ43Xbm48ZJlPtSYrAQQCtqbJt/KguGw+3WTd13SBdxi0cx8BHwaMockcJvW34xNj0J6BLtAqeMCvG74W9KLxyr0jYGXCc5OKlvj2P7fwoju+kgddyNBIVJNu8b1MCXUxWr9S4gedkByRYIJXXvqQ8mdherW/TR0TxuqxYCU2bJ0A4Q7EkgysiaxNzaQ2v8XqDY4JpZsABZwqUUuJxxGpi+MqISNFvHSOP7KQa6w+asx10Z/5pF5BIOQSFpnoSNHoj5VMyjw6hwPBccGcTW0eJbrNEhADXqziHYLEQr4tMQBspkkaOLME+YyFYFJUj2bX9s3cwnqozlEJQwkdFviF3x3BYKiE8HrCekGbbWzOKdVS6o+agaCybNIrSVXWtDaS+HqYz97OOaJMLxmXSnoNN6X853pUiJkJbJVitflX1fay0aqBKS+YG2w3yGO6CGVfChaufD+FZ7PLrXsLYk7iPOKq++LY4/8y77gAEnWa2jOW3ZeFGj3bL6cQMJ+F3H+Iysj7rSsaoIIHLQ1m3WWaYg9FtPEjQMjvoCfGu0EDBZVGp9ZVvwGkGxXjDdbKXr7W9yCg3v8mTVu14Qmx5xgAg0SbMmmlyI3Z3WNT15OE/GMntUoHbob+kUZHcZ39hhkIc7DrLKcZybxiyF6Gq8FWIQd6EiK7Mg9sxqKUQ70UuWDN7txJwGKFWqKQo+WBXpbxiODKgsI81GAAAAOElEQVR4nO3RQQkAIADAQFPZP5ofA/gRZdwlGGwMAAAAgM/N7XUHlxgcZ3CcwXEGxxkcZ3Dc6eAF11YNgR9C3cQAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<PIL.Image.Image image mode=P size=480x16 at 0x7FAE20848EE0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAeAAAAAQCAMAAAD571wKAAADAFBMVEUNFmgb5gmJVdooVlU+AG8WmBFQcucXJMoF6ZJD1sRXzTSbhs9QZcSDr6Oq43yrXwjBLtHpqCtWZBJ0klYwFqADWjqIueij/fahO3st/XHACyIxQVpFb2sbEFoWbgKJErFn/EN4wFVZo/jia7etXzwU4Q6EiSWn6sZjIPmEoc1iDyKrWd69+qtNygeF34MjgIte5FdFgTRSZfAUMjP+5DGQFVFXie3PiMl7u8ZB1ZN8ZTmAIKLlypt+sh8N3Fyra1vFV8DSLvoPaoDULI9e6x9D0VLxDWMbkvZTpURwkj+wgOdG9C4Fqbt6Cv8k3uQ43Xbm48ZJlPtSYrAQQCtqbJt/KguGw+3WTd13SBdxi0cx8BHwaMockcJvW34xNj0J6BLtAqeMCvG74W9KLxyr0jYGXCc5OKlvj2P7fwoju+kgddyNBIVJNu8b1MCXUxWr9S4gedkByRYIJXXvqQ8mdherW/TR0TxuqxYCU2bJ0A4Q7EkgysiaxNzaQ2v8XqDY4JpZsABZwqUUuJxxGpi+MqISNFvHSOP7KQa6w+asx10Z/5pF5BIOQSFpnoSNHoj5VMyjw6hwPBccGcTW0eJbrNEhADXqziHYLEQr4tMQBspkkaOLME+YyFYFJUj2bX9s3cwnqozlEJQwkdFviF3x3BYKiE8HrCekGbbWzOKdVS6o+agaCybNIrSVXWtDaS+HqYz97OOaJMLxmXSnoNN6X853pUiJkJbJVitflX1fay0aqBKS+YG2w3yGO6CGVfChaufD+FZ7PLrXsLYk7iPOKq++LY4/8y77gAEnWa2jOW3ZeFGj3bL6cQMJ+F3H+Iysj7rSsaoIIHLQ1m3WWaYg9FtPEjQMjvoCfGu0EDBZVGp9ZVvwGkGxXjDdbKXr7W9yCg3v8mTVu14Qmx5xgAg0SbMmmlyI3Z3WNT15OE/GMntUoHbob+kUZHcZ39hhkIc7DrLKcZybxiyF6Gq8FWIQd6EiK7Mg9sxqKUQ70UuWDN7txJwGKFWqKQo+WBXpbxiODKgsI81GAAAAR0lEQVR4nO3RsQ3AIADAML7i/8+6sDOhimBfECljcJW5/N3BIQbHGRxncJzBcQbHGRxn8GMMjzM4zuA4g+MMjjM4zuC43eAPpvkbgW7/0y0AAAAASUVORK5CYII=\n",
      "text/plain": [
       "<PIL.Image.Image image mode=P size=480x16 at 0x7FAE20835DF0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAeAAAAAQCAMAAAD571wKAAADAFBMVEUNFmgb5gmJVdooVlU+AG8WmBFQcucXJMoF6ZJD1sRXzTSbhs9QZcSDr6Oq43yrXwjBLtHpqCtWZBJ0klYwFqADWjqIueij/fahO3st/XHACyIxQVpFb2sbEFoWbgKJErFn/EN4wFVZo/jia7etXzwU4Q6EiSWn6sZjIPmEoc1iDyKrWd69+qtNygeF34MjgIte5FdFgTRSZfAUMjP+5DGQFVFXie3PiMl7u8ZB1ZN8ZTmAIKLlypt+sh8N3Fyra1vFV8DSLvoPaoDULI9e6x9D0VLxDWMbkvZTpURwkj+wgOdG9C4Fqbt6Cv8k3uQ43Xbm48ZJlPtSYrAQQCtqbJt/KguGw+3WTd13SBdxi0cx8BHwaMockcJvW34xNj0J6BLtAqeMCvG74W9KLxyr0jYGXCc5OKlvj2P7fwoju+kgddyNBIVJNu8b1MCXUxWr9S4gedkByRYIJXXvqQ8mdherW/TR0TxuqxYCU2bJ0A4Q7EkgysiaxNzaQ2v8XqDY4JpZsABZwqUUuJxxGpi+MqISNFvHSOP7KQa6w+asx10Z/5pF5BIOQSFpnoSNHoj5VMyjw6hwPBccGcTW0eJbrNEhADXqziHYLEQr4tMQBspkkaOLME+YyFYFJUj2bX9s3cwnqozlEJQwkdFviF3x3BYKiE8HrCekGbbWzOKdVS6o+agaCybNIrSVXWtDaS+HqYz97OOaJMLxmXSnoNN6X853pUiJkJbJVitflX1fay0aqBKS+YG2w3yGO6CGVfChaufD+FZ7PLrXsLYk7iPOKq++LY4/8y77gAEnWa2jOW3ZeFGj3bL6cQMJ+F3H+Iysj7rSsaoIIHLQ1m3WWaYg9FtPEjQMjvoCfGu0EDBZVGp9ZVvwGkGxXjDdbKXr7W9yCg3v8mTVu14Qmx5xgAg0SbMmmlyI3Z3WNT15OE/GMntUoHbob+kUZHcZ39hhkIc7DrLKcZybxiyF6Gq8FWIQd6EiK7Mg9sxqKUQ70UuWDN7txJwGKFWqKQo+WBXpbxiODKgsI81GAAAAN0lEQVR4nO3RUQkAIBTAwBfL/sn8MYIijLsEg80AAAAAXLKO3x08YnCcwXEGxxkcZ3CcwXG/B28D3Q4B9AZwcQAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<PIL.Image.Image image mode=P size=480x16 at 0x7FAE20848220>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAeAAAAAQCAMAAAD571wKAAADAFBMVEUNFmgb5gmJVdooVlU+AG8WmBFQcucXJMoF6ZJD1sRXzTSbhs9QZcSDr6Oq43yrXwjBLtHpqCtWZBJ0klYwFqADWjqIueij/fahO3st/XHACyIxQVpFb2sbEFoWbgKJErFn/EN4wFVZo/jia7etXzwU4Q6EiSWn6sZjIPmEoc1iDyKrWd69+qtNygeF34MjgIte5FdFgTRSZfAUMjP+5DGQFVFXie3PiMl7u8ZB1ZN8ZTmAIKLlypt+sh8N3Fyra1vFV8DSLvoPaoDULI9e6x9D0VLxDWMbkvZTpURwkj+wgOdG9C4Fqbt6Cv8k3uQ43Xbm48ZJlPtSYrAQQCtqbJt/KguGw+3WTd13SBdxi0cx8BHwaMockcJvW34xNj0J6BLtAqeMCvG74W9KLxyr0jYGXCc5OKlvj2P7fwoju+kgddyNBIVJNu8b1MCXUxWr9S4gedkByRYIJXXvqQ8mdherW/TR0TxuqxYCU2bJ0A4Q7EkgysiaxNzaQ2v8XqDY4JpZsABZwqUUuJxxGpi+MqISNFvHSOP7KQa6w+asx10Z/5pF5BIOQSFpnoSNHoj5VMyjw6hwPBccGcTW0eJbrNEhADXqziHYLEQr4tMQBspkkaOLME+YyFYFJUj2bX9s3cwnqozlEJQwkdFviF3x3BYKiE8HrCekGbbWzOKdVS6o+agaCybNIrSVXWtDaS+HqYz97OOaJMLxmXSnoNN6X853pUiJkJbJVitflX1fay0aqBKS+YG2w3yGO6CGVfChaufD+FZ7PLrXsLYk7iPOKq++LY4/8y77gAEnWa2jOW3ZeFGj3bL6cQMJ+F3H+Iysj7rSsaoIIHLQ1m3WWaYg9FtPEjQMjvoCfGu0EDBZVGp9ZVvwGkGxXjDdbKXr7W9yCg3v8mTVu14Qmx5xgAg0SbMmmlyI3Z3WNT15OE/GMntUoHbob+kUZHcZ39hhkIc7DrLKcZybxiyF6Gq8FWIQd6EiK7Mg9sxqKUQ70UuWDN7txJwGKFWqKQo+WBXpbxiODKgsI81GAAAARUlEQVR4nO3RUQkAIBTAwFfL/sX8sYIK4y7BYDM8sY7fHVxicJzBcQbHGRxncJzBcQbHGRxncJzBcQbHGRxncJzBcb8Gb8xSHIGWFnltAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<PIL.Image.Image image mode=P size=480x16 at 0x7FADE8405FD0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "tile = fuse_h4x.render_tile(fse, 82)\n",
    "im = fuse_h4x.display(None, tile[-2:,:])\n",
    "display(im.resize((im.width*8, im.height*8), Image.NEAREST))\n",
    "\n",
    "for mybs in clk_sweep_bs[:-1]:\n",
    "    clk_tiles = fuse_h4x.tile_bitmap(fse, mybs^clk_sweep_bs[-1])\n",
    "    ctile = clk_tiles[(5, 9, 82)].astype('uint8')\n",
    "    im = fuse_h4x.display(None, (tile*ctile)[-2:,:])\n",
    "    display(im.resize((im.width*8, im.height*8), Image.NEAREST))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So hey, when you swap a clock it keeps the routing the same, but just changes one mux. This makes sense. Now what I want to know is which columns correspond to which mux. Let's take it row by row, and see if we can tell that way."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAABMAAAAESAQAAAAAz/5S4AAAAb0lEQVR4nO3bsQnAMAwEQMWkcJkRUnrMjO7CI1hBkNw1Qjx8oV4RAAAAAMDSnjVH7QoA8GPnRlrVDAAAAAAAAAAAAAAAAAAAH9Ujrry2llcV95sfwsdGWtVcy8UAAAAAAAAAAAAAAAAAAAAAAIA8E0CQBlHShaZDAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=1216x274 at 0x7FAE2083EB50>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAABMAAAAESAQAAAAAz/5S4AAAAd0lEQVR4nO3bsQmAQAwF0DuwsLDICI7gCI7m6BaKje1BcvBe8wm/yQBJawAAAAAAAAAADHA9sSaPP/3NyB0BANKcSW1RS/YCAAAAAAAAAAAAAMA89mOreYPcv9+6oaJkW1mUbAEAAAAAAAAAAAAAAAAAAAAAgKndX0IH547vACoAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=1216x274 at 0x7FAE20835DF0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAABMAAAAESAQAAAAAz/5S4AAAAcklEQVR4nO3QsQmAUAwE0FMsdAtXcTNxAke2+K2dYPzyXnOEKxKSAAAAAAAAAAAAAAAAxfYWc/HYkbXFWDwCAL81FbUAAAAAAAAA8B1b9mTIUX3HjTNZXls2FLX98jEAAAAAAAAAAAAAAAAAAAAAAOCBC+rQB/Cxq+tWAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=1216x274 at 0x7FAE20835DF0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAABMAAAAESAQAAAAAz/5S4AAAAdElEQVR4nO3OsQ2AMBADQCtKQZkRGCWjMToFdHRIKAm6ayzLxX8CAAAAAAAAAAAAAAAAAADAwo4rtsF1HfXOPrYCAD/Up1wBAAAAXqrpSck++o+Hnj2pKV/faVOuM2tTrgAAAAAAAAAAAAAAAAAAAAAAwFpOaqQJ2YVp0w4AAAAASUVORK5CYII=\n",
      "text/plain": [
       "<PIL.Image.Image image mode=1 size=1216x274 at 0x7FAE20835DF0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "mod = codegen.Module()\n",
    "cst = codegen.Constraints()\n",
    "\n",
    "clks = [ibuf(mod, cst, p) for p in true_pins]\n",
    "\n",
    "for i in range(2, 6):\n",
    "    for j in range(2, 20):\n",
    "        flop = dff(mod, cst, i, j)\n",
    "\n",
    "bs_base, hdr, ftr, posp, config = tiled_fuzzer.run_pnr(mod, cst, {})\n",
    "\n",
    "clk_march_bs = []\n",
    "for k in range(4):\n",
    "    mod = codegen.Module()\n",
    "    cst = codegen.Constraints()\n",
    "\n",
    "    clks = [ibuf(mod, cst, p) for p in true_pins]\n",
    "\n",
    "    for i, clk in enumerate(clks[:-1]):\n",
    "        for j in range(2, 20):\n",
    "            flop = dff(mod, cst, 2+i, j)\n",
    "            if i <= k:\n",
    "                mod.assigns.append((flop, clk))\n",
    "\n",
    "    bs_tmp, hdr, ftr, posp, config = tiled_fuzzer.run_pnr(mod, cst, {})\n",
    "    clk_march_bs.append(bs_tmp)\n",
    "\n",
    "base_tmp = bs_base\n",
    "for mybs in clk_march_bs:\n",
    "    display(bslib.display(None, mybs^base_tmp))\n",
    "    base_tmp = mybs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Alriiiiight!! So As before, we got 1, 5, 13, 17 for the first mux. Then the second mux goes 0, 4, 8, 12, 16. Third 3, 7, 11, 15. And fourth 2, 6, 10, 14, 18."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAeAAAAAQCAMAAAD571wKAAADAFBMVEUNFmgb5gmJVdooVlU+AG8WmBFQcucXJMoF6ZJD1sRXzTSbhs9QZcSDr6Oq43yrXwjBLtHpqCtWZBJ0klYwFqADWjqIueij/fahO3st/XHACyIxQVpFb2sbEFoWbgKJErFn/EN4wFVZo/jia7etXzwU4Q6EiSWn6sZjIPmEoc1iDyKrWd69+qtNygeF34MjgIte5FdFgTRSZfAUMjP+5DGQFVFXie3PiMl7u8ZB1ZN8ZTmAIKLlypt+sh8N3Fyra1vFV8DSLvoPaoDULI9e6x9D0VLxDWMbkvZTpURwkj+wgOdG9C4Fqbt6Cv8k3uQ43Xbm48ZJlPtSYrAQQCtqbJt/KguGw+3WTd13SBdxi0cx8BHwaMockcJvW34xNj0J6BLtAqeMCvG74W9KLxyr0jYGXCc5OKlvj2P7fwoju+kgddyNBIVJNu8b1MCXUxWr9S4gedkByRYIJXXvqQ8mdherW/TR0TxuqxYCU2bJ0A4Q7EkgysiaxNzaQ2v8XqDY4JpZsABZwqUUuJxxGpi+MqISNFvHSOP7KQa6w+asx10Z/5pF5BIOQSFpnoSNHoj5VMyjw6hwPBccGcTW0eJbrNEhADXqziHYLEQr4tMQBspkkaOLME+YyFYFJUj2bX9s3cwnqozlEJQwkdFviF3x3BYKiE8HrCekGbbWzOKdVS6o+agaCybNIrSVXWtDaS+HqYz97OOaJMLxmXSnoNN6X853pUiJkJbJVitflX1fay0aqBKS+YG2w3yGO6CGVfChaufD+FZ7PLrXsLYk7iPOKq++LY4/8y77gAEnWa2jOW3ZeFGj3bL6cQMJ+F3H+Iysj7rSsaoIIHLQ1m3WWaYg9FtPEjQMjvoCfGu0EDBZVGp9ZVvwGkGxXjDdbKXr7W9yCg3v8mTVu14Qmx5xgAg0SbMmmlyI3Z3WNT15OE/GMntUoHbob+kUZHcZ39hhkIc7DrLKcZybxiyF6Gq8FWIQd6EiK7Mg9sxqKUQ70UuWDN7txJwGKFWqKQo+WBXpbxiODKgsI81GAAAAvElEQVR4nO3SPQ6CUBREYQrXBAq9IroO1P1vwJjMacbcXHvndLw/5X0MQ9PT8vFX0aPIz7+rXc2K9Twvin03tSnG56Kj4vc4j/0ntSrWXy3GL0Vnxf9hPedPivlR+T7G/b4m69DU+QY4wAEOcIADHOAABzjAAQ7wvwNzAR1g9QHsTexzeMBYB0AHTP4ei8UF836bBeBqcZ7Pcx7zvwIT4xWwQ45FFfQXbIADHOAABzjAAQ5wgAMc4AAH+NMbo+Ru29cQQRMAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<PIL.Image.Image image mode=P size=480x16 at 0x7FADE8405670>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAeAAAAAQCAMAAAD571wKAAADAFBMVEUNFmgb5gmJVdooVlU+AG8WmBFQcucXJMoF6ZJD1sRXzTSbhs9QZcSDr6Oq43yrXwjBLtHpqCtWZBJ0klYwFqADWjqIueij/fahO3st/XHACyIxQVpFb2sbEFoWbgKJErFn/EN4wFVZo/jia7etXzwU4Q6EiSWn6sZjIPmEoc1iDyKrWd69+qtNygeF34MjgIte5FdFgTRSZfAUMjP+5DGQFVFXie3PiMl7u8ZB1ZN8ZTmAIKLlypt+sh8N3Fyra1vFV8DSLvoPaoDULI9e6x9D0VLxDWMbkvZTpURwkj+wgOdG9C4Fqbt6Cv8k3uQ43Xbm48ZJlPtSYrAQQCtqbJt/KguGw+3WTd13SBdxi0cx8BHwaMockcJvW34xNj0J6BLtAqeMCvG74W9KLxyr0jYGXCc5OKlvj2P7fwoju+kgddyNBIVJNu8b1MCXUxWr9S4gedkByRYIJXXvqQ8mdherW/TR0TxuqxYCU2bJ0A4Q7EkgysiaxNzaQ2v8XqDY4JpZsABZwqUUuJxxGpi+MqISNFvHSOP7KQa6w+asx10Z/5pF5BIOQSFpnoSNHoj5VMyjw6hwPBccGcTW0eJbrNEhADXqziHYLEQr4tMQBspkkaOLME+YyFYFJUj2bX9s3cwnqozlEJQwkdFviF3x3BYKiE8HrCekGbbWzOKdVS6o+agaCybNIrSVXWtDaS+HqYz97OOaJMLxmXSnoNN6X853pUiJkJbJVitflX1fay0aqBKS+YG2w3yGO6CGVfChaufD+FZ7PLrXsLYk7iPOKq++LY4/8y77gAEnWa2jOW3ZeFGj3bL6cQMJ+F3H+Iysj7rSsaoIIHLQ1m3WWaYg9FtPEjQMjvoCfGu0EDBZVGp9ZVvwGkGxXjDdbKXr7W9yCg3v8mTVu14Qmx5xgAg0SbMmmlyI3Z3WNT15OE/GMntUoHbob+kUZHcZ39hhkIc7DrLKcZybxiyF6Gq8FWIQd6EiK7Mg9sxqKUQ70UuWDN7txJwGKFWqKQo+WBXpbxiODKgsI81GAAAASklEQVR4nO3SQQkAIAAEQVPZP5ofC/gQcZlJcLA3Bk/N7fUOLhE4TuA4geMEjhM4TuA4geNOAzvEZwSOEzhO4DiB4wSOEzjuduAFlj8ogZ08mxUAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<PIL.Image.Image image mode=P size=480x16 at 0x7FADE8405640>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAeAAAAAQCAMAAAD571wKAAADAFBMVEUNFmgb5gmJVdooVlU+AG8WmBFQcucXJMoF6ZJD1sRXzTSbhs9QZcSDr6Oq43yrXwjBLtHpqCtWZBJ0klYwFqADWjqIueij/fahO3st/XHACyIxQVpFb2sbEFoWbgKJErFn/EN4wFVZo/jia7etXzwU4Q6EiSWn6sZjIPmEoc1iDyKrWd69+qtNygeF34MjgIte5FdFgTRSZfAUMjP+5DGQFVFXie3PiMl7u8ZB1ZN8ZTmAIKLlypt+sh8N3Fyra1vFV8DSLvoPaoDULI9e6x9D0VLxDWMbkvZTpURwkj+wgOdG9C4Fqbt6Cv8k3uQ43Xbm48ZJlPtSYrAQQCtqbJt/KguGw+3WTd13SBdxi0cx8BHwaMockcJvW34xNj0J6BLtAqeMCvG74W9KLxyr0jYGXCc5OKlvj2P7fwoju+kgddyNBIVJNu8b1MCXUxWr9S4gedkByRYIJXXvqQ8mdherW/TR0TxuqxYCU2bJ0A4Q7EkgysiaxNzaQ2v8XqDY4JpZsABZwqUUuJxxGpi+MqISNFvHSOP7KQa6w+asx10Z/5pF5BIOQSFpnoSNHoj5VMyjw6hwPBccGcTW0eJbrNEhADXqziHYLEQr4tMQBspkkaOLME+YyFYFJUj2bX9s3cwnqozlEJQwkdFviF3x3BYKiE8HrCekGbbWzOKdVS6o+agaCybNIrSVXWtDaS+HqYz97OOaJMLxmXSnoNN6X853pUiJkJbJVitflX1fay0aqBKS+YG2w3yGO6CGVfChaufD+FZ7PLrXsLYk7iPOKq++LY4/8y77gAEnWa2jOW3ZeFGj3bL6cQMJ+F3H+Iysj7rSsaoIIHLQ1m3WWaYg9FtPEjQMjvoCfGu0EDBZVGp9ZVvwGkGxXjDdbKXr7W9yCg3v8mTVu14Qmx5xgAg0SbMmmlyI3Z3WNT15OE/GMntUoHbob+kUZHcZ39hhkIc7DrLKcZybxiyF6Gq8FWIQd6EiK7Mg9sxqKUQ70UuWDN7txJwGKFWqKQo+WBXpbxiODKgsI81GAAAASklEQVR4nO3RsQ3AMBADMW+V/TcLYGSDj10cyF5qbi2uej7T/fSHQwSOEzhO4DiB4wSOEzhOYDYB4wSOEzhO4DiB4wSOEzjur8Av7l03AZpxbx0AAAAASUVORK5CYII=\n",
      "text/plain": [
       "<PIL.Image.Image image mode=P size=480x16 at 0x7FADE8405BB0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAeAAAAAQCAMAAAD571wKAAADAFBMVEUNFmgb5gmJVdooVlU+AG8WmBFQcucXJMoF6ZJD1sRXzTSbhs9QZcSDr6Oq43yrXwjBLtHpqCtWZBJ0klYwFqADWjqIueij/fahO3st/XHACyIxQVpFb2sbEFoWbgKJErFn/EN4wFVZo/jia7etXzwU4Q6EiSWn6sZjIPmEoc1iDyKrWd69+qtNygeF34MjgIte5FdFgTRSZfAUMjP+5DGQFVFXie3PiMl7u8ZB1ZN8ZTmAIKLlypt+sh8N3Fyra1vFV8DSLvoPaoDULI9e6x9D0VLxDWMbkvZTpURwkj+wgOdG9C4Fqbt6Cv8k3uQ43Xbm48ZJlPtSYrAQQCtqbJt/KguGw+3WTd13SBdxi0cx8BHwaMockcJvW34xNj0J6BLtAqeMCvG74W9KLxyr0jYGXCc5OKlvj2P7fwoju+kgddyNBIVJNu8b1MCXUxWr9S4gedkByRYIJXXvqQ8mdherW/TR0TxuqxYCU2bJ0A4Q7EkgysiaxNzaQ2v8XqDY4JpZsABZwqUUuJxxGpi+MqISNFvHSOP7KQa6w+asx10Z/5pF5BIOQSFpnoSNHoj5VMyjw6hwPBccGcTW0eJbrNEhADXqziHYLEQr4tMQBspkkaOLME+YyFYFJUj2bX9s3cwnqozlEJQwkdFviF3x3BYKiE8HrCekGbbWzOKdVS6o+agaCybNIrSVXWtDaS+HqYz97OOaJMLxmXSnoNN6X853pUiJkJbJVitflX1fay0aqBKS+YG2w3yGO6CGVfChaufD+FZ7PLrXsLYk7iPOKq++LY4/8y77gAEnWa2jOW3ZeFGj3bL6cQMJ+F3H+Iysj7rSsaoIIHLQ1m3WWaYg9FtPEjQMjvoCfGu0EDBZVGp9ZVvwGkGxXjDdbKXr7W9yCg3v8mTVu14Qmx5xgAg0SbMmmlyI3Z3WNT15OE/GMntUoHbob+kUZHcZ39hhkIc7DrLKcZybxiyF6Gq8FWIQd6EiK7Mg9sxqKUQ70UuWDN7txJwGKFWqKQo+WBXpbxiODKgsI81GAAAATUlEQVR4nO3RoQ3AMBAEQZfl/isLCbWCXpY2M/h0ZNf6if2a2k//8EHgOIHjBI4TOE7gOIHjBGbEKbDwEQLHCRwncJzAcQLHCRx3O/ADQJFGAbM3cqEAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<PIL.Image.Image image mode=P size=480x16 at 0x7FADE8405E80>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAeAAAAAQCAMAAAD571wKAAADAFBMVEUNFmgb5gmJVdooVlU+AG8WmBFQcucXJMoF6ZJD1sRXzTSbhs9QZcSDr6Oq43yrXwjBLtHpqCtWZBJ0klYwFqADWjqIueij/fahO3st/XHACyIxQVpFb2sbEFoWbgKJErFn/EN4wFVZo/jia7etXzwU4Q6EiSWn6sZjIPmEoc1iDyKrWd69+qtNygeF34MjgIte5FdFgTRSZfAUMjP+5DGQFVFXie3PiMl7u8ZB1ZN8ZTmAIKLlypt+sh8N3Fyra1vFV8DSLvoPaoDULI9e6x9D0VLxDWMbkvZTpURwkj+wgOdG9C4Fqbt6Cv8k3uQ43Xbm48ZJlPtSYrAQQCtqbJt/KguGw+3WTd13SBdxi0cx8BHwaMockcJvW34xNj0J6BLtAqeMCvG74W9KLxyr0jYGXCc5OKlvj2P7fwoju+kgddyNBIVJNu8b1MCXUxWr9S4gedkByRYIJXXvqQ8mdherW/TR0TxuqxYCU2bJ0A4Q7EkgysiaxNzaQ2v8XqDY4JpZsABZwqUUuJxxGpi+MqISNFvHSOP7KQa6w+asx10Z/5pF5BIOQSFpnoSNHoj5VMyjw6hwPBccGcTW0eJbrNEhADXqziHYLEQr4tMQBspkkaOLME+YyFYFJUj2bX9s3cwnqozlEJQwkdFviF3x3BYKiE8HrCekGbbWzOKdVS6o+agaCybNIrSVXWtDaS+HqYz97OOaJMLxmXSnoNN6X853pUiJkJbJVitflX1fay0aqBKS+YG2w3yGO6CGVfChaufD+FZ7PLrXsLYk7iPOKq++LY4/8y77gAEnWa2jOW3ZeFGj3bL6cQMJ+F3H+Iysj7rSsaoIIHLQ1m3WWaYg9FtPEjQMjvoCfGu0EDBZVGp9ZVvwGkGxXjDdbKXr7W9yCg3v8mTVu14Qmx5xgAg0SbMmmlyI3Z3WNT15OE/GMntUoHbob+kUZHcZ39hhkIc7DrLKcZybxiyF6Gq8FWIQd6EiK7Mg9sxqKUQ70UuWDN7txJwGKFWqKQo+WBXpbxiODKgsI81GAAAAX0lEQVR4nO3RwQkAIADDQNdy/8UE8e9PYsgNUEo7RrZ5/Jqfiw6W62C5DpbrYLkOlutguQ7+HGVgSg8dyrCUHjqUYSk9dCjDUnroUIal9NChDEvpoUMZltJDhzLs6x4LgVc5AVQb+9oAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<PIL.Image.Image image mode=P size=480x16 at 0x7FADE8405760>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "tile = fuse_h4x.render_tile(fse, 82)\n",
    "im = fuse_h4x.display(None, tile[-2:,:])\n",
    "display(im.resize((im.width*8, im.height*8), Image.NEAREST))\n",
    "\n",
    "base_tmp = bs_base\n",
    "for mybs in clk_march_bs:\n",
    "    clk_tiles = fuse_h4x.tile_bitmap(fse, mybs^base_tmp)\n",
    "    ctile = clk_tiles[(5, 9, 82)].astype('uint8')\n",
    "    im = fuse_h4x.display(None, (tile*ctile)[-2:,:])\n",
    "    display(im.resize((im.width*8, im.height*8), Image.NEAREST))\n",
    "    base_tmp = mybs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(1, 1) GB00\n",
      "(2, 1) GB10\n",
      "(3, 1) GB20\n",
      "(4, 1) GB30\n"
     ]
    }
   ],
   "source": [
    "mt = fuse_h4x.tile_bitmap(fse, clk_march_bs[-1])\n",
    "interesting = {(1,1): mt[(1, 1, 12)], (2,1): mt[(2, 1, 12)], (3,1): mt[(3, 1, 12)], (4, 1): mt[(4, 1, 12)]}\n",
    "for idx, t in interesting.items():\n",
    "    row, col = idx\n",
    "    dbtile = db.grid[row][col]\n",
    "    bels, pips = gowin_unpack.parse_tile_(dbtile, t)\n",
    "    print(idx, pips['CLK0'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This seems to confirm my earlier findings. I think the main thing that needs to be done is find the boundaries of each branch. Maybe I can use one full row, and then do a sweep on the next row/clock to see which tap it connects to. I just hope it doesn't randomly alternate which clock goes where.\n",
    "\n",
    "Then I need to find some more clock sources to do it all again for the secondary clocks. And then I think it may be time to write the real fuzzer???"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "mod = codegen.Module()\n",
    "cst = codegen.Constraints()\n",
    "\n",
    "clks = [ibuf(mod, cst, p) for p in true_pins]\n",
    "\n",
    "for i in range(2, 6):\n",
    "    for j in range(2, 20):\n",
    "        flop = dff(mod, cst, i, j)\n",
    "\n",
    "bs_base, hdr, ftr, posp, config = tiled_fuzzer.run_pnr(mod, cst, {})\n",
    "\n",
    "modules = []\n",
    "constrs = []\n",
    "for k in range(4):\n",
    "    for col in range(2, 20):\n",
    "        mod = codegen.Module()\n",
    "        cst = codegen.Constraints()\n",
    "\n",
    "        clks = [ibuf(mod, cst, p) for p in true_pins]\n",
    "\n",
    "        for i, clk in enumerate(clks[:-1]):\n",
    "            for j in range(2, 20):\n",
    "                flop = dff(mod, cst, 2+i, j)\n",
    "                if i < k:\n",
    "                    mod.assigns.append((flop, clk))\n",
    "                elif i == k and j == col:\n",
    "                    mod.assigns.append((flop, clk))\n",
    "\n",
    "        modules.append(mod)\n",
    "        constrs.append(cst)\n",
    "\n",
    "p = Pool()\n",
    "pnr_res = p.map(lambda param: tiled_fuzzer.run_pnr(*param, {}), zip(modules, constrs))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{0: {1: {1, 2, 3},\n",
       "  5: {4, 5, 6, 7, 8, 9},\n",
       "  13: {10, 11, 12, 13, 14, 15},\n",
       "  17: {16, 17, 18}},\n",
       " 1: {0: {1, 2},\n",
       "  4: {3, 4, 5, 6},\n",
       "  8: {7, 8, 9},\n",
       "  12: {10, 11, 12, 13, 14},\n",
       "  16: {15, 16, 17, 18}},\n",
       " 2: {3: {1, 2, 3, 4, 5},\n",
       "  7: {6, 7, 8, 9},\n",
       "  11: {10, 11, 12, 13},\n",
       "  15: {14, 15, 16, 17, 18}},\n",
       " 3: {2: {1, 2, 3, 4},\n",
       "  6: {5, 6, 7, 8, 9},\n",
       "  10: {10, 11, 12},\n",
       "  14: {13, 14, 15, 16},\n",
       "  18: {17, 18}}}"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "clks = {}\n",
    "complete_taps = set()\n",
    "for idx, (sweep_bs, *rest) in enumerate(pnr_res):\n",
    "    #display(bslib.display(None, sweep_bs^bs_base))\n",
    "    sweep_tiles = fuse_h4x.tile_bitmap(fse, sweep_bs^bs_base)\n",
    "    \n",
    "    dff = set()\n",
    "    tap = None\n",
    "    gclk = idx//18\n",
    "    if idx and idx%18==0:\n",
    "        complete_taps.update(clks[gclk-1].keys())\n",
    "    #print(\"#\"*80)\n",
    "    for loc, tile in sweep_tiles.items():\n",
    "        row, col, ttyp = loc\n",
    "        #print(row, idx//18)\n",
    "        #print(ttyp)\n",
    "        if row <= gclk: continue\n",
    "        if ttyp == 12:\n",
    "            #print(\"branch\", col)\n",
    "            dff.add(col)\n",
    "        elif ttyp in {64, 13, 14}:\n",
    "            #print(\"tap\", col)\n",
    "            if col not in complete_taps:\n",
    "                tap = col\n",
    "    clks.setdefault(gclk, {}).setdefault(tap, set()).update(dff)\n",
    "    #print(complete_taps, clks)\n",
    "\n",
    "clks"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "I think this a pretty good place to switch to the real fuzzer. At this point it's clear which center muxes drive which spines/taps/branches, and how it all connects. The only thing that's missing is the secondary clocks, which is just more of the same. I just need to find some more clock inputs, maybe the PLL outputs?\n",
    "\n",
    "Here is a diagram of my current understanding. The muxes in the center connect a clock pin to the left or right spines. Each specific spine has 2-3 taps per side, which route it up and down. At each row this tap can then be connected to a branch that spans several colums. The different colours in this diagram represent GB00-GB40 at the slice level.\n",
    "![clocks](doc/fig/clocks.png)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
